#!/usr/bin/env python3
# _*_ coding:utf-8 _*_

import argparse
import re
import requests
import ipaddress

'''
此poc原地址为：https://github.com/rabbitmask/SB-Actuator，感谢作者！！！笔芯
'''

banner=r'''
  ___________________             _____          __                __                
 /   _____/\______   \           /  _  \   _____/  |_ __ _______ _/  |_  ___________ 
 \_____  \  |    |  _/  ______  /  /_\  \_/ ___\   __\  |  \__  \\   __\/  _ \_  __ \
 /        \ |    |   \ /_____/ /    |    \  \___|  | |  |  // __ \|  | (  <_> )  | \/
/_______  / |______  /         \____|__  /\___  >__| |____/(____  /__|  \____/|__|   
        \/         \/                  \/     \/                \/                   
                                                                      By RabbitMask | V 1.2
'''

requests.packages.urllib3.disable_warnings()

headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0",
           "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",}


# Spring Boot < 1.5 默认未授权访问所有端点
# Spring Boot >= 1.5 默认只允许访问/health和/info端点，但是此安全性通常被应用程序开发人员禁用
# 另外考虑到人为关闭默认端点开启非默认端点的情况，综上所述，此处采用暴力模式配合异步并发（子进程中嵌套异步子线程）解决。
pathlist=['/autoconfig','/beans','/configprops','/dump','/mappings','/metrics','/trace','/monitor']

#大多数Actuator仅支持GET请求并仅显示敏感的配置数据,如果使用了Jolokia端点，可能会产生XXE、甚至是RCE安全问题。
#通过查看/jolokia/list 中存在的 Mbeans，是否存在logback 库提供的reloadByURL方法来进行判断。
def Jolokiacheck(url):
    url_tar = url + '/jolokia/list'
    r = requests.get(url_tar, headers=headers, verify=False)
    if r.status_code == 200:
        print("目标站点开启了 jolokia 端点的未授权访问,路径为：{}".format(url_tar))
        # saveinfo("目标站点开启了 jolokia 端点的未授权访问,路径为：{}".format(url_tar))
        if 'reloadByURL' in r.text:
            print("目标站点开启了 jolokia 端点且存在reloadByURL方法,可进行XXE/RCE测试,路径为：{}".format(url_tar))
            # saveinfo("目标站点开启了 jolokia 端点且存在reloadByURL方法,可进行XXE/RCE测试,路径为：{}".format(url_tar))
        if 'createJNDIRealm' in r.text:
            print("目标站点开启了 jolokia 端点且存在createJNDIRealm方法,可进行JNDI注入RCE测试,路径为：{}".format(url_tar))
            # saveinfo("目标站点开启了 jolokia 端点且存在createJNDIRealm方法,可进行JNDI注入RCE测试,路径为：{}".format(url_tar))


#Spring Boot env端点存在环境属性覆盖和XStream反序列化漏洞
def Envcheck_1(url):
    url_tar = url + '/env'
    r = requests.get(url_tar, headers=headers, verify=False)
    if r.status_code == 200:
        print("目标站点开启了 env 端点的未授权访问,路径为：{}".format(url_tar))
        # saveinfo("目标站点开启了 env 端点的未授权访问,路径为：{}".format(url_tar))
        if 'spring.cloud.bootstrap.location' in r.text:
            print("目标站点开启了 env 端点且spring.cloud.bootstrap.location属性开启,可进行环境属性覆盖RCE测试,路径为：{}".format(url_tar))
            # saveinfo("目标站点开启了 env 端点且spring.cloud.bootstrap.location属性开启,可进行环境属性覆盖RCE测试,路径为：{}".format(url_tar))
        if 'eureka.client.serviceUrl.defaultZone' in r.text:
            print("目标站点开启了 env 端点且eureka.client.serviceUrl.defaultZone属性开启,可进行XStream反序列化RCE测试,路径为：{}".format(url_tar))
            # saveinfo("目标站点开启了 env 端点且eureka.client.serviceUrl.defaultZone属性开启,可进行XStream反序列化RCE测试,路径为：{}".format(url_tar))

#Spring Boot 1.x版本端点在根URL下注册。
def sb1_Actuator(url):
    key=0
    Envcheck_1(url)
    Jolokiacheck(url)
    for i in pathlist:
        url_tar = url+i
        r = requests.get(url_tar, headers=headers, verify=False)
        if r.status_code==200:
            print("目标站点开启了 {} 端点的未授权访问,路径为：{}".format(i.replace('/',''),url_tar))
            # saveinfo("目标站点开启了 {} 端点的未授权访问,路径为：{}".format(i.replace('/',''),url_tar))
            key=1
    return key

#Spring Boot 2.x版本存在H2配置不当导致的RCE，目前非正则判断，测试阶段
#另外开始我认为环境属性覆盖和XStream反序列化漏洞只有1.*版本存在
#后来证实2.*也是存在的，data需要以json格式发送，这个我后边会给出具体exp
def Envcheck_2(url):
    url_tar = url + '/actuator/env'
    r = requests.get(url_tar, headers=headers, verify=False)
    if r.status_code == 200:
        print("目标站点开启了 env 端点的未授权访问,路径为：{}".format(url_tar))
        # saveinfo("目标站点开启了 env 端点的未授权访问,路径为：{}".format(url_tar))
        if 'spring.cloud.bootstrap.location' in r.text:
            print("目标站点开启了 env 端点且spring.cloud.bootstrap.location属性开启,可进行环境属性覆盖RCE测试,路径为：{}".format(url_tar))
            # saveinfo("目标站点开启了 env 端点且spring.cloud.bootstrap.location属性开启,可进行环境属性覆盖RCE测试,路径为：{}".format(url_tar))
        if 'eureka.client.serviceUrl.defaultZone' in r.text:
            print("目标站点开启了 env 端点且eureka.client.serviceUrl.defaultZone属性开启,可进行XStream反序列化RCE测试,路径为：{}".format(url_tar))
            # saveinfo("目标站点开启了 env 端点且eureka.client.serviceUrl.defaultZone属性开启,可进行XStream反序列化RCE测试,路径为：{}".format(url_tar))
        headers["Cache-Control"]="max-age=0"
        rr = requests.post(url+'/actuator/restart', headers=headers, verify=False)
        if rr.status_code == 200:
            print("目标站点开启了 env 端点且支持restart端点访问,可进行H2 RCE测试,路径为：{}".format(url+'/actuator/restart'))
            # saveinfo("目标站点开启了 env 端点且支持restart端点访问,可进行H2 RCE测试,路径为：{}".format(url+'/actuator/restart'))



#Spring Boot 2.x版本端点移动到/actuator/路径。
def sb2_Actuator(url):
    Envcheck_2(url)
    Jolokiacheck(url+'/actuator')
    for i in pathlist:
        url_tar = url+'/actuator'+i
        r = requests.get(url_tar, headers=headers, verify=False)
        if r.status_code==200:
            print("目标站点开启了 {} 端点的未授权访问,路径为：{}".format(i.replace('/',''),url_tar))
            # saveinfo("目标站点开启了 {} 端点的未授权访问,路径为：{}".format(i.replace('/', ''), url_tar))




def sb_Actuator(url):
    try:
        if sb1_Actuator(url)==0:
            sb2_Actuator(url)
    except:
        pass



if __name__ == '__main__':
    print(banner)
    parser = argparse.ArgumentParser()
    parser.add_argument("-u", "--url", dest='url',help="单目标扫描")

    args = parser.parse_args()
    sb_Actuator(args.url)
